#include "../model/Atom.h"
#include "../model/Atom_Element.h"
#include "../model/Molecule_Visitor.h"
#include "BALL_PDB_Types_Adapter.h"

// Get all the BALL dependencies here
#include <BALL/KERNEL/atomContainer.h>
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/residue.h>
#include <BALL/KERNEL/PTE.h>

static Atom_Element get_atom_type(const BALL::Atom& a);
static Atom get_atom(const BALL::Atom& a, const int& id);


BALL_PDB_Types_Adapter::BALL_PDB_Types_Adapter(const BALL::AtomContainer& ac)
    : ac(ac) {}

/**
 * Read every atom in the molecule, then call a Molecule_Visitor for each one.
 */
void BALL_PDB_Types_Adapter::for_each_atom(Molecule_Visitor& v) const
{
    unsigned fragment_number = 0;
    const BALL::Residue* prev_residue = NULL;
    unsigned id = 0;

    for (BALL::AtomConstIterator it = ac.beginAtom(); +it; ++it, ++id)
    {
        // Get BALLs basic types
        const BALL::Atom& atom = *it;
        const BALL::Residue* residue = atom.getResidue();

        // Convert everything to FDMDE types
        const Atom_Element& type = get_atom_type(atom);
        const Atom& fdmde_atom = get_atom(atom, id);

        // TODO: Research in BALL what happens if we get two consecutive equal residues
        // (e.g two ALA)
        if (residue != prev_residue)
        {
            /* unsigned prev_fragment_number = */ ++fragment_number;
            v.on_fragment_start(/*fragment_number, prev_fragment_number*/);
            prev_residue = residue;
        }

        // Notify the visitor of the new atom available
        v.on_atom_found(&fdmde_atom, &type);
    }
}

/**
 * Private static method which knows how to translate from a BALL::Atom
 * to an FDMDE's internal representation of an atom type
 */
static Atom_Element get_atom_type(const BALL::Atom& a)
{
    return Atom_Element(a.getElement().getSymbol(),
                        a.getElement().getAtomicWeight(),
                        a.getElement().getVanDerWaalsRadius());
}

/**
 * Private static method which knows how to translate from a BALL::Atom
 * to an FDMDE's internal representation of an atom
 */
static Atom get_atom(const BALL::Atom& a, const int& id)
{
    return Atom(id, a.getFullName(), a.getPosition().x, a.getPosition().y, a.getPosition().z);
}

